#pragma once
#include<iostream>
#include<unistd.h>
#include<queue>
#include<pthread.h>

const static u_int32_t gcap = 5; // For Deubug

template<class T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _cap<=_bq.size();
    }
    bool IsEmpty()
    {
        return _bq.empty();
    }
public:
    BlockQueue(u_int32_t cap=gcap):_num_c(0),_num_p(0),_cap(cap)
    {
        pthread_mutex_init(&_lock,nullptr);
        pthread_cond_init(&_p_cond,nullptr);
        pthread_cond_init(&_c_cond,nullptr);
    }
    void Enqueue(const T& in)
    {
        pthread_mutex_lock(&_lock);
        while(IsFull())
        {
            _num_p++;
            pthread_cond_wait(&_p_cond,&_lock);
            _num_p--;
        }
        _bq.push(in);
        if(_num_c>0)
            pthread_cond_signal(&_c_cond);
        pthread_mutex_unlock(&_lock);
    }
    void Pop(T* out)
    {
        pthread_mutex_lock(&_lock);
        while(IsEmpty())
        {
            _num_c++;
            pthread_cond_wait(&_c_cond,&_lock);
            _num_c--;
        }
        *out=_bq.front();
        _bq.pop();
        if(_num_p>0)
            pthread_cond_signal(&_p_cond);
        pthread_mutex_unlock(&_lock);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_c_cond);
        pthread_cond_destroy(&_p_cond);
    }
private:
    pthread_mutex_t _lock;
    pthread_cond_t _c_cond; 
    pthread_cond_t _p_cond;

    std::queue<T> _bq;
    u_int32_t _cap;

    u_int32_t _num_c;
    u_int32_t _num_p;
};